package 中等.动态规划.其他;

/**
 * 一条包含字母 A-Z 的消息通过以下映射进行了 编码 ：
 * 'A' -> "1"
 * 'B' -> "2"
 * ...
 * 'Z' -> "26"
 * 要 解码 已编码的消息，所有数字必须基于上述映射的方法，反向映射回字母（可能有多种方法）。例如，"11106" 可以映射为：
 * "AAJF" ，将消息分组为 (1 1 10 6)
 * "KJF" ，将消息分组为 (11 10 6)
 * 注意，消息不能分组为  (1 11 06) ，因为 "06" 不能映射为 "F" ，这是由于 "6" 和 "06" 在映射中并不等价。
 * 给你一个只含数字的 非空 字符串 s ，请计算并返回 解码 方法的 总数 。
 * 题目数据保证答案肯定是一个 32 位 的整数。
 * <p>
 * 来源：力扣（LeetCode）
 * 链接：https://leetcode.cn/problems/decode-ways
 */
public class 解码方法_91 {

    public static void main(String[] args) {

        System.out.println(('6' - '0'));
        System.out.println(('6' - '0') + 1 <= 6);
        System.out.println(numDecodings("226"));

    }

    /**
     * 动态规划
     * 1，子问题：
     * 子串 [0,i] 的映射方法数量是多少
     * 2，分类讨论
     * a.如果当前数字是 0 ，那么前面的数字必须是 1 或者 2 否则，不能构成字母
     * 只有 当前数字和前面一个数字 组成一种字母的情况索引 dp[i] = dp[i-2]
     * <p>
     * b.如果当前数字和前面一个数字在 [10,26] 的范围那么可以有两种可能
     * 当前数字一种 + 当前数字和前面数字组合一种
     * dp[i]= dp[i-1]+ dp[i-2]
     * c.否则只能一种情况，当前数字
     */
    public static int numDecodings(String s) {
        if (s.charAt(0) == '0') return 0;
        char[] chars = s.toCharArray();
        int[] dp = new int[s.length()];
        dp[0] = 1;

        for (int i = 1; i < s.length(); i++) {
            if (chars[i] == '0') {
                if (chars[i - 1] - '0' > 2 || chars[i - 1] - '0' == 0) return 0;

                if (i - 2 >= 0) {
                    dp[i] = dp[i - 2];
                } else {
                    dp[i] = 1;
                }
            } else if (chars[i - 1] == '1' || (chars[i - 1] == '2' &&
                    chars[i] - '0' <= 6)) {
                if (i - 2 >= 0) {
                    dp[i] = dp[i - 2] + dp[i - 1];
                } else {
                    dp[i] = dp[i - 1] + 1;
                }
            } else {
                dp[i] = dp[i - 1];
            }
        }

        return dp[dp.length - 1];
    }

}
